/**
 * 
 */
package components.register;

import gui.ControlPanel;
import components.Util;
import components.exceptions.BinaryToIntException;
import components.exceptions.IntToBinaryException;
import components.exceptions.SubArrayException;

/**
 * @author Chz
 * 
 */
/**
 * @author Chz
 * 
 */
public class RegisterGroup {

	public static boolean changeLight = true;

	/**
	 * a set of Machine Status Code stored in MSR. By default, MSR = 0 means the
	 * machine is Halted
	 */
	public static final int MS_HALT = 0;
	public static final int MS_RUNNING = 1;
	public static final int MS_FAULT = 2;
	public static final int MS_SINGLESTEP = 3;

	/**
	 * a set of Machine Fault Code stored in MFR.
	 */
	public static final int MF_IllegalMemoryAddress = 0;
	public static final int MF_IllegalTrapCode = 1;
	public static final int MF_IllegalOperationCode = 2;

	/***
	 * a set of Conditional Code stored in CC register.
	 */
	public static final int CC_OVERFLOW = 0;
	public static final int CC_UNDERFLOW = 1;
	public static final int CC_DIVISIONALBY0 = 2;
	public static final int CC_EQUALORNOT = 3;

	/***
	 * length of each register
	 */

	public static final int PC_LEN = 16;
	public static final int CC_LEN = 4;
	public static final int IR_LEN = 16;
	public static final int MAR_LEN = 16;
	public static final int MBR_LEN = 16;
	public static final int MSR_LEN = 16;
	public static final int MFR_LEN = 4;
	public static final int X0_LEN = 16;
	public static final int OR_LEN = 5;
	public static final int ADDR_LEN = 6;
	public static final int GPR_LEN = 16;
	public static final int GPR_NUM = 4;
	public static final int RAR_LEN = 2;
	public static final int CNTR_LEN = 4;
	public static final int DEVR_LEN = 5;
	public static final int IMMR_LEN = 6;

	/*****************
	 * all registers and their definitions.
	 */
	private int[] PC = new int[PC_LEN]; // program counter
	private int[] CC = new int[CC_LEN]; // Condition Code
	// CC[0] = OVERFLOW; CC[1] = UNDERFLOW; CC[2] = DIVISIONBYZERO;
	// CC[3] = EQUALORNOT
	private int[] IR = new int[IR_LEN]; // Instruction Register
	private int[] MAR = new int[MAR_LEN]; // Memory Address Register
	private int[] MBR = new int[MBR_LEN]; // Memory Buffer Register
	private int[] MSR = new int[MSR_LEN]; // Memory Status Register
	private int[] MFR = new int[MFR_LEN]; // Memory Fault Register
	private int[] X0 = new int[X0_LEN];
	private int IX = 0;
	private int I = 0;
	private int[] OR = new int[OR_LEN];
	private int opcode = 0; // the content of opcode is same as or. listed for
							// convenience
	private int[] ADDR = new int[ADDR_LEN];
	private int[][] GPR = new int[GPR_NUM][GPR_LEN]; // R0-R3
	private int[] RAR1 = new int[RAR_LEN]; // Register Address Register 1
	private int[] RAR2 = new int[RAR_LEN]; // Register Address Register 2
	private int LRR = 0; // Left/Right register, used in shift/rotate
							// instructions
	private int ALR = 0; // Arithmetic/logic register, used in shift/rotate
							// instructions
	private int[] CNTR = new int[CNTR_LEN]; // counter register, used in
											// shift/rotate
	// instructions
	private int[] DEVR = new int[DEVR_LEN]; // devid register.

	private int[] IMMR = new int[IMMR_LEN]; // used to store immediate number

	// this class must be singleton so that we can synchronize all the data in
	// our system
	private static RegisterGroup self;

	private RegisterGroup() {
	}

	public static RegisterGroup getInstance() {
		if (self == null)
			self = new RegisterGroup();

		return self;
	}

	public int[] getPC() {
		return PC;
	}

	public void setPC(int[] pC) {
		if (changeLight)
			ControlPanel.PC_Light.refresh();

		for (int i = 0; i < PC_LEN; i++)
			PC[i] = pC[i];
	}

	public int[] getCC() {
		return CC;
	}

	public void setCC(int[] cC) {
		if (changeLight)
			ControlPanel.CC_Light.refresh();

		for (int i = 0; i < CC_LEN; i++)
			CC[i] = cC[i];
	}

	/**
	 * alternative methods of get Conditional Code. the parameter is the index
	 * of conditional code
	 * 
	 * @return referred conditional code
	 */
	public int getCC(int index) {
		return CC[index];
	}

	/**
	 * alternative methods of set Conditional Code. the first parameter is the
	 * index of conditional code, the second parameter is the value.
	 * 
	 */
	public void setCC(int index, int conditionalCode) {
		CC[index] = conditionalCode;
	}

	public int[] getIR() {
		return IR;
	}

	public void setIR(int[] iR) {
		if (changeLight)
			ControlPanel.IR_Light.refresh();

		for (int i = 0; i < IR_LEN; i++)
			IR[i] = iR[i];
	}

	public int[] getMAR() {
		return MAR;
	}

	public void setMAR(int[] mAR) {

		if (changeLight)
			ControlPanel.MAR_Light.refresh();

		int srcLen = mAR.length;
		int dstLen = MAR_LEN;
		int i = 0, j = 0;
		if (srcLen == dstLen) {
			for (int tmp = 0; tmp < MAR_LEN; tmp++)
				MAR[tmp] = mAR[tmp];
		} else if (srcLen > dstLen) {
			try {
				MAR = Util.subArray(mAR, srcLen - dstLen, srcLen - 1);
			} catch (SubArrayException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {// srcLen < dstLen
			for (i = 0; i < dstLen - srcLen; i++)
				MAR[i] = 0;
			for (j = 0; j < srcLen; j++)
				MAR[i + j] = mAR[j];
		}

	}

	public int[] getMBR() {
		return MBR;
	}

	public void setMBR(int[] mBR) {

		if (changeLight)
			ControlPanel.MBR_Light.refresh();
		int srcLen = mBR.length;
		int dstLen = MBR_LEN;
		int i = 0, j = 0;
		if (srcLen == dstLen) {
			for (int tmp = 0; tmp < MAR_LEN; tmp++)
				MBR[tmp] = mBR[tmp];
		} else if (srcLen > dstLen) {
			try {
				MBR = Util.subArray(mBR, srcLen - dstLen, srcLen - 1);
			} catch (SubArrayException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {// srcLen < dstLen
			for (i = 0; i < dstLen - srcLen + 1; i++)
				MBR[i] = 0;
			for (j = 0; j < srcLen; j++)
				MBR[i + j] = mBR[j];
		}
	}

	public int[] getMSR() {
		return MSR;
	}

	public void setMSR(int[] mSR) {
		if (changeLight)
			ControlPanel.MSR_Light.refresh();
		for (int i = 0; i < MSR_LEN; i++)
			MSR[i] = mSR[i];
	}

	public void setMSR(int msr) {
		if (changeLight)
			ControlPanel.MSR_Light.refresh();
		try {
			MSR = Util.intToBinary(msr, MSR.length);
		} catch (IntToBinaryException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public int[] getMFR() {
		return MFR;
	}

	public void setMFR(int[] mFR) {
		if (changeLight)
			ControlPanel.MFR_Light.refresh();
		for (int i = 0; i < MFR_LEN; i++)
			MFR[i] = mFR[i];
	}

	public void setMFR(int mfr) {
		if (changeLight)
			ControlPanel.MFR_Light.refresh();
		try {
			MFR = Util.intToBinary(mfr, MFR.length);
		} catch (IntToBinaryException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public int[] getX0() {
		return X0;
	}

	public void setX0(int[] x0) {

		for (int i = 0; i < x0.length; i++)
			X0[i] = x0[i];
	}

	public int getIX() {
		return IX;
	}

	public void setIX(int iX) {

		IX = iX;
	}

	public int getI() {
		return I;
	}

	public void setI(int i) {
		I = i;
	}

	public int[] getOR() {
		return OR;
	}

	public void setOR(int[] oR) {
		if (changeLight)
			ControlPanel.OR_Light.refresh();
		for (int i = 0; i < OR_LEN; i++)
			OR[i] = oR[i];
	}

	public int[] getIMMR() {
		return IMMR;
	}

	public void setIMMR(int[] iMMR) {
		for (int i = 0; i < IMMR_LEN; i++)
			IMMR[i] = iMMR[i];
	}

	public int[] getADDR() {

		return ADDR;
	}

	public void setADDR(int[] aDDR) {
		for (int i = 0; i < ADDR_LEN; i++)
			ADDR[i] = aDDR[i];
	}

	public int[] getGPR(int i) {

		return GPR[i];
	}

	/***
	 * 
	 * @param gPR
	 *            is the content going to put into GPR[index]
	 * @param index
	 */
	public void setGPR(int[] gPR, int index) {

		if (changeLight) {
			ControlPanel.R1_Light.refresh();
			ControlPanel.R2_Light.refresh();
			ControlPanel.R3_Light.refresh();
			ControlPanel.R4_Light.refresh();
		}
		GPR[index] = gPR;
	}

	public int[] getRAR1() {
		return RAR1;
	}

	/***
	 * return the content of rar1 register in int type.
	 * 
	 * @return rar1
	 */
	public int getRAR1InInt() {
		int tmp = 0;
		try {
			tmp = Util.binaryToInt(RAR1);
		} catch (BinaryToIntException e) {
			System.err.println("fatal error in RAR1 register");
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return tmp;
	}

	public void setRAR1(int[] rAR1) {
		if (changeLight)
			ControlPanel.RAR1_Light.refresh();
		for (int i = 0; i < RAR_LEN; i++)
			RAR1[i] = rAR1[i];
	}

	public int[] getRAR2() {
		return RAR2;
	}

	/***
	 * return the content of rar2 register in int type.
	 * 
	 * @return rar2
	 */
	public int getRAR2InInt() {
		int tmp = 0;
		try {
			tmp = Util.binaryToInt(RAR2);
		} catch (BinaryToIntException e) {
			System.err.println("fatal error in RAR2 register");
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return tmp;
	}

	public void setRAR2(int[] rAR2) {
		if (changeLight)
			ControlPanel.RAR2_Light.refresh();
		for (int i = 0; i < RAR_LEN; i++)
			RAR2[i] = rAR2[i];
	}

	public int getLRR() {
		return LRR;
	}

	public void setLRR(int lRR) {
		LRR = lRR;
	}

	public int getALR() {
		return ALR;
	}

	public void setALR(int aLR) {
		ALR = aLR;
	}

	/**
	 * @return the cNTR
	 */
	public int[] getCNTR() {
		return CNTR;
	}

	public int getCNTRInInt() {
		int ret = 0;
		try {
			ret = Util.binaryToInt(CNTR);
		} catch (BinaryToIntException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return ret;
	}

	/**
	 * @param cNTR
	 *            the cNTR to set
	 */
	public void setCNTR(int[] cNTR) {
		for (int i = 0; i < CNTR_LEN; i++)
			CNTR[i] = cNTR[i];
	}

	public int[] getDEVR() {
		return DEVR;
	}

	public void setDEVR(int[] dEVR) {
		if (changeLight)
			ControlPanel.DEVR_Light.refresh();
		for (int i = 0; i < DEVR_LEN; i++)
			DEVR[i] = dEVR[i];
	}

	public int getOpcode() {
		return opcode;
	}

	public void setOpcode(int opcode) {
		this.opcode = opcode;
	}

}
